To draw insights from the data using visualization techniques!

Dataset Attributes :

show_id : Unique ID for every Movie / Tv Show

type : Identifier - A Movie or TV Show

title : Title of the Movie / Tv Show

director : Director of the Movie

cast : Actors involved in the movie / show

country : Country where the movie / show was produced

date_added : Date it was added on Netflix

release_year : Actual Release year of the move / show

rating : TV Rating of the movie / show

duration : Total Duration - in minutes or number of seasons

listed_in : Genre

description : The summary description

Loading Library and Data Reading

library(tidyverse)
Warning: package ‘tidyverse’ was built under R version 4.2.2
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────── tidyverse 1.3.2 ──
✔ tibble  3.1.8     ✔ purrr   0.3.5
✔ readr   2.1.3     ✔ forcats 0.5.2
Warning: package ‘tibble’ was built under R version 4.2.2
Warning: package ‘readr’ was built under R version 4.2.2
Warning: package ‘purrr’ was built under R version 4.2.2
Warning: package ‘forcats’ was built under R version 4.2.2
── Conflicts ─────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ data.table::between() masks dplyr::between()
✖ dplyr::filter()       masks stats::filter()
✖ data.table::first()   masks dplyr::first()
✖ dplyr::lag()          masks stats::lag()
✖ data.table::last()    masks dplyr::last()
✖ purrr::transpose()    masks data.table::transpose()
library(dplyr)
library(ggplot2)
library(readr)
library(tibble)
library(plotly)
Warning: package ‘plotly’ was built under R version 4.2.2

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
Netflix <- read_csv("netflix_titles.csv")
Rows: 8807 Columns: 12
── Column specification ─────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (11): show_id, type, title, director, cast, country, date_added, rating, duration, li...
dbl  (1): release_year

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
View(Netflix)
head(Netflix)

In the data set there are 8,807 observation of 12 variables describe the Tv shows,cast,director,release year, rating and many more.

Data cleaning

As a first step we can remove uninformative variables from the dataset. In our case it is a show_id varaible. The description variable will not be used for the exploratory data analysis, but can be used to find similar movies and tv shows using the text similarities.

#drop show_id column
Netflix = subset(Netflix, select = -c(show_id) )

Descriptive Summary

library(modelsummary)
Warning: package ‘modelsummary’ was built under R version 4.2.2
datasummary((` Type` = type) ~ N + Percent(), data = Netflix, title = "Netflix Contnet Type")
Netflix Contnet Type
Type N Percent
Movie 6131 69.62
TV Show 2676 30.38
# Data summary for rating 
datasummary((`Rating` = rating )~ N + Percent(), data = Netflix, title = "Rating Categories")
Rating Categories
Rating N Percent
66 min 1 0.01
74 min 1 0.01
84 min 1 0.01
G 41 0.47
NC-17 3 0.03
NR 80 0.91
PG 287 3.26
PG-13 490 5.56
R 799 9.07
TV-14 2160 24.53
TV-G 220 2.50
TV-MA 3207 36.41
TV-PG 863 9.80
TV-Y 307 3.49
TV-Y7 334 3.79
TV-Y7-FV 6 0.07
UR 3 0.03
#print number of missing values for each variable
data.frame("variable"=c(colnames(Netflix)), "missing values count"=sapply(Netflix, function(x) sum(is.na(x))), row.names=NULL)

From the above output we see that we have missing values for variables director, cast, country, data_added,rating and duration. Since rating is the categorical variable with 14 levels we can fill in (approximate) the missing values for rating with a mode.

#function to find a mode
getmode <- function(v) {
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}
Netflix$rating[is.na(Netflix$rating)] <- getmode(Netflix$rating)

We can change the date format of the data_added varible for easier manipulations further.

Netflix$date_added <- as.Date(Netflix$date_added, format = "%B %d, %Y")

The missing values for the variables director, cast and country, date_added can not be easily approximated, so for now we are going to continue without filling them. We are going to drop the missing values, at point where it will be necessary. We also drop duplicated rows in the dataset based on the title, country, type, release_year variables

#drop duplicated rows based on the title, country, type and release_year
Netflix=distinct(Netflix,title,country,type,release_year, .keep_all= TRUE)

We have done the data cleaning steps and can continue with exploring the data.

DATA VISUALISATION

Amount Of Netflix by Content

content_by_type <- Netflix%>% group_by(type) %>% 
  summarise(count = n())
# In ggplot2 library, the code is created by two parts. First one is ggplot(), here we have to specify our arguments such as data, x and y axis and fill type. then continue with + and type of the graph will be added by using geom_graphytype.
Netflix_fig1  <- ggplot(data = content_by_type, aes(x= type, y= count, fill= type))+
  geom_bar(colour ="black", fill = "Blue" ,  stat = "identity")+
  guides(fill= FALSE)+
  xlab("Netflix Content by Type") + ylab("Amount of Netflix Content")+
  ggtitle("Amount of Netflix Content By Type")
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2
3.3.4.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Netflix_fig1

As we see from above there are more than 2 times more Movies than TV Shows on Netflix.

Since many movies and tv shows are made by several countries (country variable), to correctly count the total amount of content produced by each country we need to split strings in country variable and count the total occurence of each country on its own.

Amount of Netflix Content by Top 13 Country

# 1: split the countries (ex: "United States, India, South Korea, China" form to 'United States' 'India' 'South Korea' 'China') in the country column by using strsplit() function and then assign this operation to "k" for future use.
s <- strsplit(Netflix$country, split = ", ")

# 2: Created a new data frame by using data.frame() function. First column should be type = second one country=. Created type column by using rep() function. The function replicates the values in netds$type depends on the length of each element of s. we used sapply()) function. Now s is our new data in sapply().

Netflix_countries_fuul <- data.frame(type = rep(Netflix$type, sapply(s, length)), country = unlist(s))
# 3: Changed the elements of country column as character by using as.charachter() function.

Netflix_countries_fuul$country <- as.character(gsub(",","",Netflix_countries_fuul$country))

# 4: we created new grouped data frame by the name of amount_by_country NA.omit() function deletes the NA values on the country column/variable. Then we groupped countries and types by using group_by() function (in the "dplyr" library).

amount_by_country <- na.omit(Netflix_countries_fuul) %>%
  group_by(country, type) %>%
  summarise(count = n())
`summarise()` has grouped output by 'country'. You can override using the `.groups`
argument.
# 5: we can use the "amount_by_country" data frame to observe number of TV Show or Movie in countries. However, this list is too big to be visualized. Thus, we will create a new data frame as table to see just top 10 countries by the name of "w".

w <- reshape(data=data.frame(amount_by_country),idvar="country",
                          v.names = "count",
                          timevar = "type",
                          direction="wide") %>% arrange(desc(count.Movie)) %>%
                          top_n(13)
Selecting by count.TV Show
# 6: names of the second and third columns are changed by using names() function as seen below.

names(w)[2] <- "number_of_movie"
names(w)[3] <- "number_of_tv_show"

# 7: In the arrange() function we sorted our count.movie columns as descending but, now, we want to change this sort depends on the total values of "number of Movies" and "number of TV Shows". To sort a data frame in R, use the order() function. By default, sorting is ASCENDING. Therefore, we have to specify as descending. + is used to specify total operation.

w <- w[order(desc(w$number_of_movie +w$number_of_tv_show)),]

# 8: Now we can create our graph by using ggplot2 library.

library(ggplot2)
Netflix_Fig2 <- ggplot(w, aes(number_of_movie, number_of_tv_show, colour=country))+ 
  geom_point(size=5)+
  xlab("Number of Movies") + ylab("Number of TV Shows")+
  ggtitle("Amount of Netflix Content By Top 13 Country")
ggplotly(Netflix_Fig2, dynamicTicks = T)
NA

We can clearly see that United state is a clear on top in the Amount of content on Netflix. Countries as japan, South Korea, Taiwan having more TV shoes as compared to Movies.

Amount of Netflix content By Time

#new_date is added to visualise the data more easy 

df1 = Netflix %>% group_by(date_added) %>% summarise(added_today = n()) %>% 
  mutate(total_number_of_content = cumsum(added_today), type = "Total")

df_by_date <- df1 %>% group_by(date_added,type) %>% summarise(added_today = n()) %>% ungroup() %>% group_by(type) %>% mutate(total_number_of_content = cumsum(added_today))
`summarise()` has grouped output by 'date_added'. You can override using the `.groups`
argument.
#Using rbind() function represents a row bind function for vectors, data frames, and matrices to be arranged as rows.
#common = intersect(colnames(df1), colnames(df_by_date))
#full_data<- rbind(df1[common], df_by_date[common])
full_data <- rbind(as.data.frame(df1), as.data.frame(df_by_date))
View(full_data)

Netflix_Fig3 <- plot_ly(full_data, x = ~date_added, y = ~total_number_of_content, color = ~type, type = 'scatter', mode = 'lines', colors=c("#399ba3",  "#9addbd", "#bd3939"))
library(ggplot2)

Netflix_Fig3 <- Netflix_Fig3 %>% layout(yaxis = list(title = 'Count'), xaxis = list(title = 'Date'), title="Amout Of Content As A Function Of Time")
Netflix_Fig3

We notice how fast the amount of movies on Netflix overcame the amount of TV Shows.

Amount of Content by Rating

library(plotly)
df_by_rating_full = Netflix %>% group_by(rating) %>% summarise(count = n())
Netflix_fig4 = plot_ly(df_by_rating_full, labels = ~rating, values = ~count, type = 'pie')
Netflix_fig4 = Netflix_fig4 %>% layout(title = 'Amount of content of Rating', xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))
Netflix_fig4

The TV-MA rating is used to create the most content. A television program that was only intended for mature audiences is given the TV-MA rating by the TV Parental Guidelines.

The second-largest category is TV-14, which refers to material that can be unsuitable for minors under the age of 14.

The incredibly popular R rating comes in third place. The Motion Picture Association of America determines that an R-rated film contains material that would be inappropriate for children under the age of 17; the MPAA states that “Under 17 requires accompanying parent or adult guardian.”

Amount of content Rating (Movie vs Tv shows)

df_by_rating_full = Netflix %>% group_by(rating,type) %>% summarise(count = n())
`summarise()` has grouped output by 'rating'. You can override using the `.groups` argument.
names(df_by_rating_full) [1] <- "rating"
names(df_by_rating_full) [2] <- "type"
names(df_by_rating_full) [3] <- "content"
newdata2 <- reshape(data=data.frame(df_by_rating_full),idvar="rating",
                          v.names = "content",
                          timevar = "type",
                          direction="wide")
names(newdata2)[2] <- "Movie"
names(newdata2)[3] <- "TV Show"
newdata2$`TV Show`[is.na(newdata2$`TV Show`)] <- print(0)
[1] 0
# visualisation
library(plotly)
rating <- newdata2$rating
Movie <- newdata2$Movie
Tv_Show <- newdata2$`TV Show`
Netflix_fig5 = plot_ly(newdata2, x = ~rating, y = ~Movie, type = 'bar', name = 'Movie', marker = list(color = '#bd3939'))
Netflix_fig5 <- Netflix_fig5 %>% add_trace(y = ~Tv_Show, name = 'TV Show', marker = list(color = '#399ba3'))
Netflix_fig5 <- Netflix_fig5 %>% layout(yaxis = list(title = 'Count'),
                        barmode = 'stack', 
                        title="Amount of Content By Rating (Movie vs. TV Show)")
Netflix_fig5

Which countries are producing most shows

library(ggplot2)
Netflix_fig6 = Netflix  %>% group_by(type)  %>% mutate(country = fct_infreq(country)) %>% ggplot(aes(x = country)) + 
            geom_histogram(stat = 'count') + facet_wrap(~type, scales = 'free_x') + 
            theme_bw() + coord_cartesian(xlim = c(1,10)) + scale_x_discrete(labels = function(x){str_wrap(x,20)}, breaks = function(x) {x[1:10]})
Warning in geom_histogram(stat = "count") :
  Ignoring unknown parameters: `binwidth`, `bins`, and `pad`
Netflix_fig6

View(Netflix_fig6)

From the above we can see that :

  1. After United States, India is the largest source of Movies listed on Netflix.
  2. There is no India Tv Shows as much Indian Movies

Top Genres on Netflix

Netflix_generes = strsplit(Netflix$listed_in, split = ", ")
genres_listed_in <- data.frame(type = rep(Netflix$type, sapply(Netflix_generes, length)), 
                               listed_in = unlist(Netflix_generes))
genres_listed_in$listed_in <- as.character(gsub(",","",genres_listed_in$listed_in))

df_list  = genres_listed_in %>% 
  group_by(type, listed_in) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count)) %>% top_n(10)
`summarise()` has grouped output by 'type'. You can override using the `.groups` argument.
Selecting by count
Netlfix_fig7 = plot_ly(df_list, x = ~listed_in, y = ~count,
                       type = 'bar', color = ~type,
                       colors = c("#bd3939", "#399ba3")) %>%
  layout(xaxis = list(categoryorder = "array", 
                      categoryarray = df_list$listed_in, 
                      title = 'Genre',
                      tickangle = 45), 
         yaxis = list(title = 'Count'), 
         title = "Top Genres (Movie vs. TV Show)", margin = list(t = 54),
         legend = list(x = 100, y = 0.5))
Netlfix_fig7

We observe that the most popular genre in both movies and TV shows is international content, which is followed by dramas and comedies. These are the top three categories on Netflix with the most content!

How are the generes clustered

library(tm)
Warning: package ‘tm’ was built under R version 4.2.2
Loading required package: NLP

Attaching package: ‘NLP’

The following object is masked from ‘package:ggplot2’:

    annotate
# building corpus
corpus <- Corpus(VectorSource(Netflix$listed_in))

# create term document matrix
tdm <- TermDocumentMatrix(corpus, 
                          control = list(minWordLength=c(1,Inf)))
# convert to matrix
m <- as.matrix(tdm)

# Hierarchical word clustering using dendrogram
distance <- dist(scale(m))
hc <- hclust(distance, method = "ward.D")
#fviz_dend(hc, cex = 0.5, k = 4, color_labels_by_k = TRUE)
#fviz_dend(hc, cex = 0.7, lwd = 0.5, k = 5,rect = TRUE,rect_fill = TRUE,type = "circular",ylab ="")
#fviz_dend(hc)
# Circular
library(dendextend)
Warning: package ‘dendextend’ was built under R version 4.2.2

---------------------
Welcome to dendextend version 1.16.0
Type citation('dendextend') for how to cite the package.

Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/

Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
You may ask questions at stackoverflow, use the r and dendextend tags: 
     https://stackoverflow.com/questions/tagged/dendextend

    To suppress this message use:  suppressPackageStartupMessages(library(dendextend))
---------------------


Attaching package: ‘dendextend’

The following object is masked from ‘package:data.table’:

    set

The following object is masked from ‘package:stats’:

    cutree
#install.packages("dplyr")
library(dplyr)
require(factoextra)
Loading required package: factoextra
Warning: package ‘factoextra’ was built under R version 4.2.2
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
Circ = fviz_dend(hc, cex = 0.7, lwd = 0.5, k = 5,
                 rect = TRUE,
                 k_colors = c("#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"),
                 rect_border = c("#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"),
                 rect_fill = TRUE,
                 type = "circular",
                 ylab = "")
Circ

The clusters of genres are depicted in the image below. The clusters show that family and kid-friendly movies are popular. Moreover, the largest genre cluster includes several other genres as well as thrillers, crimes, horror, and reality.

Movie Duration in Top 12 Countries

movie_duration = na.omit(Netflix[Netflix$type == "Movie",][,c("country", "duration")])
Duration<- strsplit(movie_duration$country, split = ", ")
duration_full <- data.frame(duration = rep(movie_duration$duration,
                                           sapply(Duration, length)),
                            country = unlist(Duration))
duration_full$duration <- as.numeric(gsub(" min","", duration_full$duration))

duration_full_subset <- duration_full[duration_full$country %in% 
                                        c("United States", "India", "United Kingdom",
                                          "Canada", "France", "Japan", "Spain", "South Korea",
                                          "Mexico", "Australia", "China", "Taiwan"),]

Netflix_fig8 = plot_ly(duration_full_subset, y = ~duration, color = ~country, type = "box") %>%
  layout(xaxis = list(title = "Country"), 
         yaxis = list(title = 'Duration (in min)'),
         title = "Box-Plots of Movie Duration in Top 12 Countries", margin = list(t = 54),
         legend = list(x = 100, y = 0.5))
Netflix_fig8
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Conclusion

It is clear that movies play a bigger role in Netflix content based on the visualization and text analysis of the Netflix data. Additionally, the data summary and visualization showed that the United States was the sole country at the time that Netflix began donating its content, which was in 2008. Additionally, Netflix TV series and movies are rated, and these ratings are consistent for various audiences. The examination of runtime and ranting categories reveals that movies aimed towards youngsters are typically shorter.

LS0tDQp0aXRsZTogIk5ldGZsaXg6IERhdGEgVmlzdWFsaXNhdGlvbiBhbmQgS2V5IEluc2lnaHQiDQphdXRob3I6ICJBbWlzaGEgR2FyZyAmIEJoYXZhbmEgQmFuZGFydSINCmRhdGU6ICIwOS0xMi0yMDIyIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29sbGFwc2VkOiBmYWxzZQ0KLS0tDQoNCg0KKipUbyBkcmF3IGluc2lnaHRzIGZyb20gdGhlIGRhdGEgdXNpbmcgdmlzdWFsaXphdGlvbiB0ZWNobmlxdWVzISoqDQoNCiMjIyAqKkRhdGFzZXQgQXR0cmlidXRlcyA6KioNCg0KKipzaG93X2lkKiogOiBVbmlxdWUgSUQgZm9yIGV2ZXJ5IE1vdmllIC8gVHYgU2hvdw0KDQoqKnR5cGUqKiA6IElkZW50aWZpZXIgLSBBIE1vdmllIG9yIFRWIFNob3cNCg0KKip0aXRsZSoqIDogVGl0bGUgb2YgdGhlIE1vdmllIC8gVHYgU2hvdw0KDQoqKmRpcmVjdG9yKiogOiBEaXJlY3RvciBvZiB0aGUgTW92aWUNCg0KKipjYXN0KiogOiBBY3RvcnMgaW52b2x2ZWQgaW4gdGhlIG1vdmllIC8gc2hvdw0KDQoqKmNvdW50cnkqKiA6IENvdW50cnkgd2hlcmUgdGhlIG1vdmllIC8gc2hvdyB3YXMgcHJvZHVjZWQNCg0KKipkYXRlX2FkZGVkKiogOiBEYXRlIGl0IHdhcyBhZGRlZCBvbiBOZXRmbGl4DQoNCioqcmVsZWFzZV95ZWFyKiogOiBBY3R1YWwgUmVsZWFzZSB5ZWFyIG9mIHRoZSBtb3ZlIC8gc2hvdw0KDQoqKnJhdGluZyoqIDogVFYgUmF0aW5nIG9mIHRoZSBtb3ZpZSAvIHNob3cNCg0KKipkdXJhdGlvbioqIDogVG90YWwgRHVyYXRpb24gLSBpbiBtaW51dGVzIG9yIG51bWJlciBvZiBzZWFzb25zDQoNCioqbGlzdGVkX2luKiogOiBHZW5yZQ0KDQoqKmRlc2NyaXB0aW9uKiogOiBUaGUgc3VtbWFyeSBkZXNjcmlwdGlvbg0KDQojIyMgKipMb2FkaW5nIExpYnJhcnkgYW5kIERhdGEgUmVhZGluZyoqDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkocGxvdGx5KQ0KTmV0ZmxpeCA8LSByZWFkX2NzdigibmV0ZmxpeF90aXRsZXMuY3N2IikNClZpZXcoTmV0ZmxpeCkNCmBgYA0KYGBge3J9DQpoZWFkKE5ldGZsaXgpDQpgYGANCg0KSW4gdGhlIGRhdGEgc2V0IHRoZXJlIGFyZSA4LDgwNyBvYnNlcnZhdGlvbiBvZiAxMiB2YXJpYWJsZXMgZGVzY3JpYmUgdGhlIFR2IHNob3dzLGNhc3QsZGlyZWN0b3IscmVsZWFzZSB5ZWFyLCByYXRpbmcgYW5kIG1hbnkgbW9yZS4NCg0KIyMjICoqRGF0YSBjbGVhbmluZyoqDQpBcyBhIGZpcnN0IHN0ZXAgd2UgY2FuIHJlbW92ZSB1bmluZm9ybWF0aXZlIHZhcmlhYmxlcyBmcm9tIHRoZSBkYXRhc2V0LiBJbiBvdXIgY2FzZSBpdCBpcyBhIHNob3dfaWQgdmFyYWlibGUuIFRoZSBkZXNjcmlwdGlvbiB2YXJpYWJsZSB3aWxsIG5vdCBiZSB1c2VkIGZvciB0aGUgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcywgYnV0IGNhbiBiZSB1c2VkIHRvIGZpbmQgc2ltaWxhciBtb3ZpZXMgYW5kIHR2IHNob3dzIHVzaW5nIHRoZSB0ZXh0IHNpbWlsYXJpdGllcy4NCg0KYGBge3J9DQojZHJvcCBzaG93X2lkIGNvbHVtbg0KTmV0ZmxpeCA9IHN1YnNldChOZXRmbGl4LCBzZWxlY3QgPSAtYyhzaG93X2lkKSApDQpgYGANCg0KDQojIyMgKipEZXNjcmlwdGl2ZSBTdW1tYXJ5KioNCmBgYHtyfQ0KbGlicmFyeShtb2RlbHN1bW1hcnkpDQpkYXRhc3VtbWFyeSgoYCBUeXBlYCA9IHR5cGUpIH4gTiArIFBlcmNlbnQoKSwgZGF0YSA9IE5ldGZsaXgsIHRpdGxlID0gIk5ldGZsaXggQ29udG5ldCBUeXBlIikNCmBgYA0KYGBge3J9DQojIERhdGEgc3VtbWFyeSBmb3IgcmF0aW5nIA0KZGF0YXN1bW1hcnkoKGBSYXRpbmdgID0gcmF0aW5nICl+IE4gKyBQZXJjZW50KCksIGRhdGEgPSBOZXRmbGl4LCB0aXRsZSA9ICJSYXRpbmcgQ2F0ZWdvcmllcyIpDQpgYGANCg0KDQpgYGB7cn0NCiNwcmludCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUNCmRhdGEuZnJhbWUoInZhcmlhYmxlIj1jKGNvbG5hbWVzKE5ldGZsaXgpKSwgIm1pc3NpbmcgdmFsdWVzIGNvdW50Ij1zYXBwbHkoTmV0ZmxpeCwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSksIHJvdy5uYW1lcz1OVUxMKQ0KYGBgDQoNCkZyb20gdGhlIGFib3ZlIG91dHB1dCB3ZSBzZWUgdGhhdCB3ZSBoYXZlIG1pc3NpbmcgdmFsdWVzIGZvciB2YXJpYWJsZXMgZGlyZWN0b3IsIGNhc3QsIGNvdW50cnksIGRhdGFfYWRkZWQscmF0aW5nIGFuZCBkdXJhdGlvbi4gU2luY2UgcmF0aW5nIGlzIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIDE0IGxldmVscyB3ZSBjYW4gZmlsbCBpbiAoYXBwcm94aW1hdGUpIHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgcmF0aW5nIHdpdGggYSBtb2RlLg0KDQpgYGB7cn0NCiNmdW5jdGlvbiB0byBmaW5kIGEgbW9kZQ0KZ2V0bW9kZSA8LSBmdW5jdGlvbih2KSB7DQogICB1bmlxdiA8LSB1bmlxdWUodikNCiAgIHVuaXF2W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh2LCB1bmlxdikpKV0NCn0NCk5ldGZsaXgkcmF0aW5nW2lzLm5hKE5ldGZsaXgkcmF0aW5nKV0gPC0gZ2V0bW9kZShOZXRmbGl4JHJhdGluZykNCmBgYA0KDQpXZSBjYW4gY2hhbmdlIHRoZSBkYXRlIGZvcm1hdCBvZiB0aGUgZGF0YV9hZGRlZCB2YXJpYmxlIGZvciBlYXNpZXIgbWFuaXB1bGF0aW9ucyBmdXJ0aGVyLg0KDQpgYGB7cn0NCk5ldGZsaXgkZGF0ZV9hZGRlZCA8LSBhcy5EYXRlKE5ldGZsaXgkZGF0ZV9hZGRlZCwgZm9ybWF0ID0gIiVCICVkLCAlWSIpDQpgYGANCg0KVGhlIG1pc3NpbmcgdmFsdWVzIGZvciB0aGUgdmFyaWFibGVzIGRpcmVjdG9yLCBjYXN0IGFuZCBjb3VudHJ5LCBkYXRlX2FkZGVkIGNhbiBub3QgYmUgZWFzaWx5IGFwcHJveGltYXRlZCwgc28gZm9yIG5vdyB3ZSBhcmUgZ29pbmcgdG8gY29udGludWUgd2l0aG91dCBmaWxsaW5nIHRoZW0uIFdlIGFyZSBnb2luZyB0byBkcm9wIHRoZSBtaXNzaW5nIHZhbHVlcywgYXQgcG9pbnQgd2hlcmUgaXQgd2lsbCBiZSBuZWNlc3NhcnkuIFdlIGFsc28gZHJvcCBkdXBsaWNhdGVkIHJvd3MgaW4gdGhlIGRhdGFzZXQgYmFzZWQgb24gdGhlIHRpdGxlLCBjb3VudHJ5LCB0eXBlLCByZWxlYXNlX3llYXIgdmFyaWFibGVzDQoNCmBgYHtyfQ0KI2Ryb3AgZHVwbGljYXRlZCByb3dzIGJhc2VkIG9uIHRoZSB0aXRsZSwgY291bnRyeSwgdHlwZSBhbmQgcmVsZWFzZV95ZWFyDQpOZXRmbGl4PWRpc3RpbmN0KE5ldGZsaXgsdGl0bGUsY291bnRyeSx0eXBlLHJlbGVhc2VfeWVhciwgLmtlZXBfYWxsPSBUUlVFKQ0KYGBgDQoNCldlIGhhdmUgZG9uZSB0aGUgZGF0YSBjbGVhbmluZyBzdGVwcyBhbmQgY2FuIGNvbnRpbnVlIHdpdGggZXhwbG9yaW5nIHRoZSBkYXRhLg0KDQojIyMgKipEQVRBIFZJU1VBTElTQVRJT04qKg0KDQojIyMgKipBbW91bnQgT2YgTmV0ZmxpeCBieSBDb250ZW50KioNCg0KYGBge3J9DQpjb250ZW50X2J5X3R5cGUgPC0gTmV0ZmxpeCU+JSBncm91cF9ieSh0eXBlKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCiMgSW4gZ2dwbG90MiBsaWJyYXJ5LCB0aGUgY29kZSBpcyBjcmVhdGVkIGJ5IHR3byBwYXJ0cy4gRmlyc3Qgb25lIGlzIGdncGxvdCgpLCBoZXJlIHdlIGhhdmUgdG8gc3BlY2lmeSBvdXIgYXJndW1lbnRzIHN1Y2ggYXMgZGF0YSwgeCBhbmQgeSBheGlzIGFuZCBmaWxsIHR5cGUuIHRoZW4gY29udGludWUgd2l0aCArIGFuZCB0eXBlIG9mIHRoZSBncmFwaCB3aWxsIGJlIGFkZGVkIGJ5IHVzaW5nIGdlb21fZ3JhcGh5dHlwZS4NCk5ldGZsaXhfZmlnMSAgPC0gZ2dwbG90KGRhdGEgPSBjb250ZW50X2J5X3R5cGUsIGFlcyh4PSB0eXBlLCB5PSBjb3VudCwgZmlsbD0gdHlwZSkpKw0KICBnZW9tX2Jhcihjb2xvdXIgPSJibGFjayIsIGZpbGwgPSAiQmx1ZSIgLCAgc3RhdCA9ICJpZGVudGl0eSIpKw0KICBndWlkZXMoZmlsbD0gRkFMU0UpKw0KICB4bGFiKCJOZXRmbGl4IENvbnRlbnQgYnkgVHlwZSIpICsgeWxhYigiQW1vdW50IG9mIE5ldGZsaXggQ29udGVudCIpKw0KICBnZ3RpdGxlKCJBbW91bnQgb2YgTmV0ZmxpeCBDb250ZW50IEJ5IFR5cGUiKQ0KTmV0ZmxpeF9maWcxDQpgYGANCg0KQXMgd2Ugc2VlIGZyb20gYWJvdmUgdGhlcmUgYXJlIG1vcmUgdGhhbiAyIHRpbWVzIG1vcmUgTW92aWVzIHRoYW4gVFYgU2hvd3Mgb24gTmV0ZmxpeC4NCg0KU2luY2UgbWFueSBtb3ZpZXMgYW5kIHR2IHNob3dzIGFyZSBtYWRlIGJ5IHNldmVyYWwgY291bnRyaWVzIChjb3VudHJ5IHZhcmlhYmxlKSwgdG8gY29ycmVjdGx5IGNvdW50IHRoZSB0b3RhbCBhbW91bnQgb2YgY29udGVudCBwcm9kdWNlZCBieSBlYWNoIGNvdW50cnkgd2UgbmVlZCB0byBzcGxpdCBzdHJpbmdzIGluIGNvdW50cnkgdmFyaWFibGUgYW5kIGNvdW50IHRoZSB0b3RhbCBvY2N1cmVuY2Ugb2YgZWFjaCBjb3VudHJ5IG9uIGl0cyBvd24uDQoNCiMjIyAqKkFtb3VudCBvZiBOZXRmbGl4IENvbnRlbnQgYnkgVG9wIDEzIENvdW50cnkqKg0KDQpgYGB7cn0NCiMgMTogc3BsaXQgdGhlIGNvdW50cmllcyAoZXg6ICJVbml0ZWQgU3RhdGVzLCBJbmRpYSwgU291dGggS29yZWEsIENoaW5hIiBmb3JtIHRvICdVbml0ZWQgU3RhdGVzJyAnSW5kaWEnICdTb3V0aCBLb3JlYScgJ0NoaW5hJykgaW4gdGhlIGNvdW50cnkgY29sdW1uIGJ5IHVzaW5nIHN0cnNwbGl0KCkgZnVuY3Rpb24gYW5kIHRoZW4gYXNzaWduIHRoaXMgb3BlcmF0aW9uIHRvICJrIiBmb3IgZnV0dXJlIHVzZS4NCnMgPC0gc3Ryc3BsaXQoTmV0ZmxpeCRjb3VudHJ5LCBzcGxpdCA9ICIsICIpDQoNCiMgMjogQ3JlYXRlZCBhIG5ldyBkYXRhIGZyYW1lIGJ5IHVzaW5nIGRhdGEuZnJhbWUoKSBmdW5jdGlvbi4gRmlyc3QgY29sdW1uIHNob3VsZCBiZSB0eXBlID0gc2Vjb25kIG9uZSBjb3VudHJ5PS4gQ3JlYXRlZCB0eXBlIGNvbHVtbiBieSB1c2luZyByZXAoKSBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIHJlcGxpY2F0ZXMgdGhlIHZhbHVlcyBpbiBuZXRkcyR0eXBlIGRlcGVuZHMgb24gdGhlIGxlbmd0aCBvZiBlYWNoIGVsZW1lbnQgb2Ygcy4gd2UgdXNlZCBzYXBwbHkoKSkgZnVuY3Rpb24uIE5vdyBzIGlzIG91ciBuZXcgZGF0YSBpbiBzYXBwbHkoKS4NCg0KTmV0ZmxpeF9jb3VudHJpZXNfZnV1bCA8LSBkYXRhLmZyYW1lKHR5cGUgPSByZXAoTmV0ZmxpeCR0eXBlLCBzYXBwbHkocywgbGVuZ3RoKSksIGNvdW50cnkgPSB1bmxpc3QocykpDQojIDM6IENoYW5nZWQgdGhlIGVsZW1lbnRzIG9mIGNvdW50cnkgY29sdW1uIGFzIGNoYXJhY3RlciBieSB1c2luZyBhcy5jaGFyYWNodGVyKCkgZnVuY3Rpb24uDQoNCk5ldGZsaXhfY291bnRyaWVzX2Z1dWwkY291bnRyeSA8LSBhcy5jaGFyYWN0ZXIoZ3N1YigiLCIsIiIsTmV0ZmxpeF9jb3VudHJpZXNfZnV1bCRjb3VudHJ5KSkNCg0KIyA0OiB3ZSBjcmVhdGVkIG5ldyBncm91cGVkIGRhdGEgZnJhbWUgYnkgdGhlIG5hbWUgb2YgYW1vdW50X2J5X2NvdW50cnkgTkEub21pdCgpIGZ1bmN0aW9uIGRlbGV0ZXMgdGhlIE5BIHZhbHVlcyBvbiB0aGUgY291bnRyeSBjb2x1bW4vdmFyaWFibGUuIFRoZW4gd2UgZ3JvdXBwZWQgY291bnRyaWVzIGFuZCB0eXBlcyBieSB1c2luZyBncm91cF9ieSgpIGZ1bmN0aW9uIChpbiB0aGUgImRwbHlyIiBsaWJyYXJ5KS4NCg0KYW1vdW50X2J5X2NvdW50cnkgPC0gbmEub21pdChOZXRmbGl4X2NvdW50cmllc19mdXVsKSAlPiUNCiAgZ3JvdXBfYnkoY291bnRyeSwgdHlwZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KIyA1OiB3ZSBjYW4gdXNlIHRoZSAiYW1vdW50X2J5X2NvdW50cnkiIGRhdGEgZnJhbWUgdG8gb2JzZXJ2ZSBudW1iZXIgb2YgVFYgU2hvdyBvciBNb3ZpZSBpbiBjb3VudHJpZXMuIEhvd2V2ZXIsIHRoaXMgbGlzdCBpcyB0b28gYmlnIHRvIGJlIHZpc3VhbGl6ZWQuIFRodXMsIHdlIHdpbGwgY3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgYXMgdGFibGUgdG8gc2VlIGp1c3QgdG9wIDEwIGNvdW50cmllcyBieSB0aGUgbmFtZSBvZiAidyIuDQoNCncgPC0gcmVzaGFwZShkYXRhPWRhdGEuZnJhbWUoYW1vdW50X2J5X2NvdW50cnkpLGlkdmFyPSJjb3VudHJ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdi5uYW1lcyA9ICJjb3VudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWV2YXIgPSAidHlwZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbj0id2lkZSIpICU+JSBhcnJhbmdlKGRlc2MoY291bnQuTW92aWUpKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24oMTMpDQoNCiMgNjogbmFtZXMgb2YgdGhlIHNlY29uZCBhbmQgdGhpcmQgY29sdW1ucyBhcmUgY2hhbmdlZCBieSB1c2luZyBuYW1lcygpIGZ1bmN0aW9uIGFzIHNlZW4gYmVsb3cuDQoNCm5hbWVzKHcpWzJdIDwtICJudW1iZXJfb2ZfbW92aWUiDQpuYW1lcyh3KVszXSA8LSAibnVtYmVyX29mX3R2X3Nob3ciDQoNCiMgNzogSW4gdGhlIGFycmFuZ2UoKSBmdW5jdGlvbiB3ZSBzb3J0ZWQgb3VyIGNvdW50Lm1vdmllIGNvbHVtbnMgYXMgZGVzY2VuZGluZyBidXQsIG5vdywgd2Ugd2FudCB0byBjaGFuZ2UgdGhpcyBzb3J0IGRlcGVuZHMgb24gdGhlIHRvdGFsIHZhbHVlcyBvZiAibnVtYmVyIG9mIE1vdmllcyIgYW5kICJudW1iZXIgb2YgVFYgU2hvd3MiLiBUbyBzb3J0IGEgZGF0YSBmcmFtZSBpbiBSLCB1c2UgdGhlIG9yZGVyKCkgZnVuY3Rpb24uIEJ5IGRlZmF1bHQsIHNvcnRpbmcgaXMgQVNDRU5ESU5HLiBUaGVyZWZvcmUsIHdlIGhhdmUgdG8gc3BlY2lmeSBhcyBkZXNjZW5kaW5nLiArIGlzIHVzZWQgdG8gc3BlY2lmeSB0b3RhbCBvcGVyYXRpb24uDQoNCncgPC0gd1tvcmRlcihkZXNjKHckbnVtYmVyX29mX21vdmllICt3JG51bWJlcl9vZl90dl9zaG93KSksXQ0KDQojIDg6IE5vdyB3ZSBjYW4gY3JlYXRlIG91ciBncmFwaCBieSB1c2luZyBnZ3Bsb3QyIGxpYnJhcnkuDQoNCmxpYnJhcnkoZ2dwbG90MikNCk5ldGZsaXhfRmlnMiA8LSBnZ3Bsb3QodywgYWVzKG51bWJlcl9vZl9tb3ZpZSwgbnVtYmVyX29mX3R2X3Nob3csIGNvbG91cj1jb3VudHJ5KSkrIA0KICBnZW9tX3BvaW50KHNpemU9NSkrDQogIHhsYWIoIk51bWJlciBvZiBNb3ZpZXMiKSArIHlsYWIoIk51bWJlciBvZiBUViBTaG93cyIpKw0KICBnZ3RpdGxlKCJBbW91bnQgb2YgTmV0ZmxpeCBDb250ZW50IEJ5IFRvcCAxMyBDb3VudHJ5IikNCmdncGxvdGx5KE5ldGZsaXhfRmlnMiwgZHluYW1pY1RpY2tzID0gVCkNCg0KYGBgDQpXZSBjYW4gY2xlYXJseSBzZWUgdGhhdCBVbml0ZWQgc3RhdGUgaXMgYSBjbGVhciBvbiB0b3AgaW4gdGhlIEFtb3VudCBvZiBjb250ZW50IG9uIE5ldGZsaXguIENvdW50cmllcyBhcyBqYXBhbiwgU291dGggS29yZWEsIFRhaXdhbiBoYXZpbmcgbW9yZSBUViBzaG9lcyBhcyBjb21wYXJlZCB0byBNb3ZpZXMuDQoNCiMjIyAqKkFtb3VudCBvZiBOZXRmbGl4IGNvbnRlbnQgQnkgVGltZSoqDQoNCmBgYHtyfQ0KI25ld19kYXRlIGlzIGFkZGVkIHRvIHZpc3VhbGlzZSB0aGUgZGF0YSBtb3JlIGVhc3kgDQoNCmRmMSA9IE5ldGZsaXggJT4lIGdyb3VwX2J5KGRhdGVfYWRkZWQpICU+JSBzdW1tYXJpc2UoYWRkZWRfdG9kYXkgPSBuKCkpICU+JSANCiAgbXV0YXRlKHRvdGFsX251bWJlcl9vZl9jb250ZW50ID0gY3Vtc3VtKGFkZGVkX3RvZGF5KSwgdHlwZSA9ICJUb3RhbCIpDQoNCmRmX2J5X2RhdGUgPC0gZGYxICU+JSBncm91cF9ieShkYXRlX2FkZGVkLHR5cGUpICU+JSBzdW1tYXJpc2UoYWRkZWRfdG9kYXkgPSBuKCkpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHR5cGUpICU+JSBtdXRhdGUodG90YWxfbnVtYmVyX29mX2NvbnRlbnQgPSBjdW1zdW0oYWRkZWRfdG9kYXkpKQ0KDQojVXNpbmcgcmJpbmQoKSBmdW5jdGlvbiByZXByZXNlbnRzIGEgcm93IGJpbmQgZnVuY3Rpb24gZm9yIHZlY3RvcnMsIGRhdGEgZnJhbWVzLCBhbmQgbWF0cmljZXMgdG8gYmUgYXJyYW5nZWQgYXMgcm93cy4NCiNjb21tb24gPSBpbnRlcnNlY3QoY29sbmFtZXMoZGYxKSwgY29sbmFtZXMoZGZfYnlfZGF0ZSkpDQojZnVsbF9kYXRhPC0gcmJpbmQoZGYxW2NvbW1vbl0sIGRmX2J5X2RhdGVbY29tbW9uXSkNCmZ1bGxfZGF0YSA8LSByYmluZChhcy5kYXRhLmZyYW1lKGRmMSksIGFzLmRhdGEuZnJhbWUoZGZfYnlfZGF0ZSkpDQpWaWV3KGZ1bGxfZGF0YSkNCg0KTmV0ZmxpeF9GaWczIDwtIHBsb3RfbHkoZnVsbF9kYXRhLCB4ID0gfmRhdGVfYWRkZWQsIHkgPSB+dG90YWxfbnVtYmVyX29mX2NvbnRlbnQsIGNvbG9yID0gfnR5cGUsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBjb2xvcnM9YygiIzM5OWJhMyIsICAiIzlhZGRiZCIsICIjYmQzOTM5IikpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCk5ldGZsaXhfRmlnMyA8LSBOZXRmbGl4X0ZpZzMgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnQ291bnQnKSwgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0RhdGUnKSwgdGl0bGU9IkFtb3V0IE9mIENvbnRlbnQgQXMgQSBGdW5jdGlvbiBPZiBUaW1lIikNCk5ldGZsaXhfRmlnMw0KYGBgDQpXZSAgbm90aWNlIGhvdyBmYXN0IHRoZSBhbW91bnQgb2YgbW92aWVzIG9uIE5ldGZsaXggb3ZlcmNhbWUgdGhlIGFtb3VudCBvZiBUViBTaG93cy4NCg0KIyMjICoqQW1vdW50IG9mIENvbnRlbnQgYnkgUmF0aW5nKioNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQpkZl9ieV9yYXRpbmdfZnVsbCA9IE5ldGZsaXggJT4lIGdyb3VwX2J5KHJhdGluZykgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCk5ldGZsaXhfZmlnNCA9IHBsb3RfbHkoZGZfYnlfcmF0aW5nX2Z1bGwsIGxhYmVscyA9IH5yYXRpbmcsIHZhbHVlcyA9IH5jb3VudCwgdHlwZSA9ICdwaWUnKQ0KTmV0ZmxpeF9maWc0ID0gTmV0ZmxpeF9maWc0ICU+JSBsYXlvdXQodGl0bGUgPSAnQW1vdW50IG9mIGNvbnRlbnQgb2YgUmF0aW5nJywgeGF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpLA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpKQ0KTmV0ZmxpeF9maWc0DQpgYGANClRoZSBUVi1NQSByYXRpbmcgaXMgdXNlZCB0byBjcmVhdGUgdGhlIG1vc3QgY29udGVudC4gQSB0ZWxldmlzaW9uIHByb2dyYW0gdGhhdCB3YXMgb25seSBpbnRlbmRlZCBmb3IgbWF0dXJlIGF1ZGllbmNlcyBpcyBnaXZlbiB0aGUgVFYtTUEgcmF0aW5nIGJ5IHRoZSBUViBQYXJlbnRhbCBHdWlkZWxpbmVzLg0KDQpUaGUgc2Vjb25kLWxhcmdlc3QgY2F0ZWdvcnkgaXMgVFYtMTQsIHdoaWNoIHJlZmVycyB0byBtYXRlcmlhbCB0aGF0IGNhbiBiZSB1bnN1aXRhYmxlIGZvciBtaW5vcnMgdW5kZXIgdGhlIGFnZSBvZiAxNC4NCg0KVGhlIGluY3JlZGlibHkgcG9wdWxhciBSIHJhdGluZyBjb21lcyBpbiB0aGlyZCBwbGFjZS4gVGhlIE1vdGlvbiBQaWN0dXJlIEFzc29jaWF0aW9uIG9mIEFtZXJpY2EgZGV0ZXJtaW5lcyB0aGF0IGFuIFItcmF0ZWQgZmlsbSBjb250YWlucyBtYXRlcmlhbCB0aGF0IHdvdWxkIGJlIGluYXBwcm9wcmlhdGUgZm9yIGNoaWxkcmVuIHVuZGVyIHRoZSBhZ2Ugb2YgMTc7IHRoZSBNUEFBIHN0YXRlcyB0aGF0ICJVbmRlciAxNyByZXF1aXJlcyBhY2NvbXBhbnlpbmcgcGFyZW50IG9yIGFkdWx0IGd1YXJkaWFuLiINCg0KIyMjICoqQW1vdW50IG9mIGNvbnRlbnQgUmF0aW5nIChNb3ZpZSB2cyBUdiBzaG93cykqKg0KDQpgYGB7cn0NCmRmX2J5X3JhdGluZ19mdWxsID0gTmV0ZmxpeCAlPiUgZ3JvdXBfYnkocmF0aW5nLHR5cGUpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQpuYW1lcyhkZl9ieV9yYXRpbmdfZnVsbCkgWzFdIDwtICJyYXRpbmciDQpuYW1lcyhkZl9ieV9yYXRpbmdfZnVsbCkgWzJdIDwtICJ0eXBlIg0KbmFtZXMoZGZfYnlfcmF0aW5nX2Z1bGwpIFszXSA8LSAiY29udGVudCINCm5ld2RhdGEyIDwtIHJlc2hhcGUoZGF0YT1kYXRhLmZyYW1lKGRmX2J5X3JhdGluZ19mdWxsKSxpZHZhcj0icmF0aW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdi5uYW1lcyA9ICJjb250ZW50IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXZhciA9ICJ0eXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uPSJ3aWRlIikNCm5hbWVzKG5ld2RhdGEyKVsyXSA8LSAiTW92aWUiDQpuYW1lcyhuZXdkYXRhMilbM10gPC0gIlRWIFNob3ciDQpuZXdkYXRhMiRgVFYgU2hvd2BbaXMubmEobmV3ZGF0YTIkYFRWIFNob3dgKV0gPC0gcHJpbnQoMCkNCiMgdmlzdWFsaXNhdGlvbg0KbGlicmFyeShwbG90bHkpDQpyYXRpbmcgPC0gbmV3ZGF0YTIkcmF0aW5nDQpNb3ZpZSA8LSBuZXdkYXRhMiRNb3ZpZQ0KVHZfU2hvdyA8LSBuZXdkYXRhMiRgVFYgU2hvd2ANCk5ldGZsaXhfZmlnNSA9IHBsb3RfbHkobmV3ZGF0YTIsIHggPSB+cmF0aW5nLCB5ID0gfk1vdmllLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnTW92aWUnLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJyNiZDM5MzknKSkNCk5ldGZsaXhfZmlnNSA8LSBOZXRmbGl4X2ZpZzUgJT4lIGFkZF90cmFjZSh5ID0gflR2X1Nob3csIG5hbWUgPSAnVFYgU2hvdycsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAnIzM5OWJhMycpKQ0KTmV0ZmxpeF9maWc1IDwtIE5ldGZsaXhfZmlnNSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdDb3VudCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYmFybW9kZSA9ICdzdGFjaycsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9IkFtb3VudCBvZiBDb250ZW50IEJ5IFJhdGluZyAoTW92aWUgdnMuIFRWIFNob3cpIikNCk5ldGZsaXhfZmlnNQ0KYGBgDQoNCg0KIyMjICoqV2hpY2ggY291bnRyaWVzIGFyZSBwcm9kdWNpbmcgbW9zdCBzaG93cyoqDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KTmV0ZmxpeF9maWc2ID0gTmV0ZmxpeCAgJT4lIGdyb3VwX2J5KHR5cGUpICAlPiUgbXV0YXRlKGNvdW50cnkgPSBmY3RfaW5mcmVxKGNvdW50cnkpKSAlPiUgZ2dwbG90KGFlcyh4ID0gY291bnRyeSkpICsgDQogICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gJ2NvdW50JykgKyBmYWNldF93cmFwKH50eXBlLCBzY2FsZXMgPSAnZnJlZV94JykgKyANCiAgICAgICAgICAgIHRoZW1lX2J3KCkgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMSwxMCkpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KXtzdHJfd3JhcCh4LDIwKX0sIGJyZWFrcyA9IGZ1bmN0aW9uKHgpIHt4WzE6MTBdfSkNCk5ldGZsaXhfZmlnNg0KDQpgYGANCkZyb20gdGhlIGFib3ZlIHdlIGNhbiBzZWUgdGhhdCA6DQoNCjEpIEFmdGVyIFVuaXRlZCBTdGF0ZXMsIEluZGlhIGlzIHRoZSBsYXJnZXN0IHNvdXJjZSBvZiBNb3ZpZXMgbGlzdGVkIG9uIE5ldGZsaXguDQoyKSBUaGVyZSBpcyBubyBJbmRpYSBUdiBTaG93cyBhcyBtdWNoIEluZGlhbiBNb3ZpZXMgDQoNCg0KDQoNCiMjIyAqKlRvcCBHZW5yZXMgb24gTmV0ZmxpeCoqDQpgYGB7cn0NCk5ldGZsaXhfZ2VuZXJlcyA9IHN0cnNwbGl0KE5ldGZsaXgkbGlzdGVkX2luLCBzcGxpdCA9ICIsICIpDQpnZW5yZXNfbGlzdGVkX2luIDwtIGRhdGEuZnJhbWUodHlwZSA9IHJlcChOZXRmbGl4JHR5cGUsIHNhcHBseShOZXRmbGl4X2dlbmVyZXMsIGxlbmd0aCkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ZWRfaW4gPSB1bmxpc3QoTmV0ZmxpeF9nZW5lcmVzKSkNCmdlbnJlc19saXN0ZWRfaW4kbGlzdGVkX2luIDwtIGFzLmNoYXJhY3Rlcihnc3ViKCIsIiwiIixnZW5yZXNfbGlzdGVkX2luJGxpc3RlZF9pbikpDQoNCmRmX2xpc3QgID0gZ2VucmVzX2xpc3RlZF9pbiAlPiUgDQogIGdyb3VwX2J5KHR5cGUsIGxpc3RlZF9pbikgJT4lIA0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSANCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lIHRvcF9uKDEwKQ0KDQpOZXRsZml4X2ZpZzcgPSBwbG90X2x5KGRmX2xpc3QsIHggPSB+bGlzdGVkX2luLCB5ID0gfmNvdW50LA0KICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ2JhcicsIGNvbG9yID0gfnR5cGUsDQogICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoIiNiZDM5MzkiLCAiIzM5OWJhMyIpKSAlPiUNCiAgbGF5b3V0KHhheGlzID0gbGlzdChjYXRlZ29yeW9yZGVyID0gImFycmF5IiwgDQogICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IGRmX2xpc3QkbGlzdGVkX2luLCANCiAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdHZW5yZScsDQogICAgICAgICAgICAgICAgICAgICAgdGlja2FuZ2xlID0gNDUpLCANCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdDb3VudCcpLCANCiAgICAgICAgIHRpdGxlID0gIlRvcCBHZW5yZXMgKE1vdmllIHZzLiBUViBTaG93KSIsIG1hcmdpbiA9IGxpc3QodCA9IDU0KSwNCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDEwMCwgeSA9IDAuNSkpDQpOZXRsZml4X2ZpZzcNCmBgYA0KV2Ugb2JzZXJ2ZSB0aGF0IHRoZSBtb3N0IHBvcHVsYXIgZ2VucmUgaW4gYm90aCBtb3ZpZXMgYW5kIFRWIHNob3dzIGlzIGludGVybmF0aW9uYWwgY29udGVudCwgd2hpY2ggaXMgZm9sbG93ZWQgYnkgZHJhbWFzIGFuZCBjb21lZGllcy4gVGhlc2UgYXJlIHRoZSB0b3AgdGhyZWUgY2F0ZWdvcmllcyBvbiBOZXRmbGl4IHdpdGggdGhlIG1vc3QgY29udGVudCENCg0KDQojIyMgKipIb3cgYXJlIHRoZSBnZW5lcmVzIGNsdXN0ZXJlZCoqDQoNCmBgYHtyfQ0KbGlicmFyeSh0bSkNCiMgYnVpbGRpbmcgY29ycHVzDQpjb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShOZXRmbGl4JGxpc3RlZF9pbikpDQoNCiMgY3JlYXRlIHRlcm0gZG9jdW1lbnQgbWF0cml4DQp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KG1pbldvcmRMZW5ndGg9YygxLEluZikpKQ0KIyBjb252ZXJ0IHRvIG1hdHJpeA0KbSA8LSBhcy5tYXRyaXgodGRtKQ0KDQojIEhpZXJhcmNoaWNhbCB3b3JkIGNsdXN0ZXJpbmcgdXNpbmcgZGVuZHJvZ3JhbQ0KZGlzdGFuY2UgPC0gZGlzdChzY2FsZShtKSkNCmhjIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gIndhcmQuRCIpDQojZnZpel9kZW5kKGhjLCBjZXggPSAwLjUsIGsgPSA0LCBjb2xvcl9sYWJlbHNfYnlfayA9IFRSVUUpDQojZnZpel9kZW5kKGhjLCBjZXggPSAwLjcsIGx3ZCA9IDAuNSwgayA9IDUscmVjdCA9IFRSVUUscmVjdF9maWxsID0gVFJVRSx0eXBlID0gImNpcmN1bGFyIix5bGFiID0iIikNCiNmdml6X2RlbmQoaGMpDQojIENpcmN1bGFyDQpsaWJyYXJ5KGRlbmRleHRlbmQpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCnJlcXVpcmUoZmFjdG9leHRyYSkNCkNpcmMgPSBmdml6X2RlbmQoaGMsIGNleCA9IDAuNywgbHdkID0gMC41LCBrID0gNSwNCiAgICAgICAgICAgICAgICAgcmVjdCA9IFRSVUUsDQogICAgICAgICAgICAgICAgIGtfY29sb3JzID0gYygiIzQ0MDE1NCIsICIjM2I1MjhiIiwgIiMyMTkxOGMiLCAiIzVlYzk2MiIsICIjZmRlNzI1IiksDQogICAgICAgICAgICAgICAgIHJlY3RfYm9yZGVyID0gYygiIzQ0MDE1NCIsICIjM2I1MjhiIiwgIiMyMTkxOGMiLCAiIzVlYzk2MiIsICIjZmRlNzI1IiksDQogICAgICAgICAgICAgICAgIHJlY3RfZmlsbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgIHR5cGUgPSAiY2lyY3VsYXIiLA0KICAgICAgICAgICAgICAgICB5bGFiID0gIiIpDQpDaXJjDQpgYGANClRoZSBjbHVzdGVycyBvZiBnZW5yZXMgYXJlIGRlcGljdGVkIGluIHRoZSBpbWFnZSBiZWxvdy4gVGhlIGNsdXN0ZXJzIHNob3cgdGhhdCBmYW1pbHkgYW5kIGtpZC1mcmllbmRseSBtb3ZpZXMgYXJlIHBvcHVsYXIuIE1vcmVvdmVyLCB0aGUgbGFyZ2VzdCBnZW5yZSBjbHVzdGVyIGluY2x1ZGVzIHNldmVyYWwgb3RoZXIgZ2VucmVzIGFzIHdlbGwgYXMgdGhyaWxsZXJzLCBjcmltZXMsIGhvcnJvciwgYW5kIHJlYWxpdHkuDQoNCiMjIyAqKk1vdmllIER1cmF0aW9uIGluIFRvcCAxMiBDb3VudHJpZXMqKg0KDQpgYGB7cn0NCm1vdmllX2R1cmF0aW9uID0gbmEub21pdChOZXRmbGl4W05ldGZsaXgkdHlwZSA9PSAiTW92aWUiLF1bLGMoImNvdW50cnkiLCAiZHVyYXRpb24iKV0pDQpEdXJhdGlvbjwtIHN0cnNwbGl0KG1vdmllX2R1cmF0aW9uJGNvdW50cnksIHNwbGl0ID0gIiwgIikNCmR1cmF0aW9uX2Z1bGwgPC0gZGF0YS5mcmFtZShkdXJhdGlvbiA9IHJlcChtb3ZpZV9kdXJhdGlvbiRkdXJhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYXBwbHkoRHVyYXRpb24sIGxlbmd0aCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cnkgPSB1bmxpc3QoRHVyYXRpb24pKQ0KZHVyYXRpb25fZnVsbCRkdXJhdGlvbiA8LSBhcy5udW1lcmljKGdzdWIoIiBtaW4iLCIiLCBkdXJhdGlvbl9mdWxsJGR1cmF0aW9uKSkNCg0KZHVyYXRpb25fZnVsbF9zdWJzZXQgPC0gZHVyYXRpb25fZnVsbFtkdXJhdGlvbl9mdWxsJGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJVbml0ZWQgU3RhdGVzIiwgIkluZGlhIiwgIlVuaXRlZCBLaW5nZG9tIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDYW5hZGEiLCAiRnJhbmNlIiwgIkphcGFuIiwgIlNwYWluIiwgIlNvdXRoIEtvcmVhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZXhpY28iLCAiQXVzdHJhbGlhIiwgIkNoaW5hIiwgIlRhaXdhbiIpLF0NCg0KTmV0ZmxpeF9maWc4ID0gcGxvdF9seShkdXJhdGlvbl9mdWxsX3N1YnNldCwgeSA9IH5kdXJhdGlvbiwgY29sb3IgPSB+Y291bnRyeSwgdHlwZSA9ICJib3giKSAlPiUNCiAgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJDb3VudHJ5IiksIA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0R1cmF0aW9uIChpbiBtaW4pJyksDQogICAgICAgICB0aXRsZSA9ICJCb3gtUGxvdHMgb2YgTW92aWUgRHVyYXRpb24gaW4gVG9wIDEyIENvdW50cmllcyIsIG1hcmdpbiA9IGxpc3QodCA9IDU0KSwNCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDEwMCwgeSA9IDAuNSkpDQpOZXRmbGl4X2ZpZzgNCmBgYA0KDQoNCg0KDQojIyMgKipDb25jbHVzaW9uKioNCg0KSXQgaXMgY2xlYXIgdGhhdCBtb3ZpZXMgcGxheSBhIGJpZ2dlciByb2xlIGluIE5ldGZsaXggY29udGVudCBiYXNlZCBvbiB0aGUgdmlzdWFsaXphdGlvbiBhbmQgdGV4dCBhbmFseXNpcyBvZiB0aGUgTmV0ZmxpeCBkYXRhLiBBZGRpdGlvbmFsbHksIHRoZSBkYXRhIHN1bW1hcnkgYW5kIHZpc3VhbGl6YXRpb24gc2hvd2VkIHRoYXQgdGhlIFVuaXRlZCBTdGF0ZXMgd2FzIHRoZSBzb2xlIGNvdW50cnkgYXQgdGhlIHRpbWUgdGhhdCBOZXRmbGl4IGJlZ2FuIGRvbmF0aW5nIGl0cyBjb250ZW50LCB3aGljaCB3YXMgaW4gMjAwOC4gQWRkaXRpb25hbGx5LCBOZXRmbGl4IFRWIHNlcmllcyBhbmQgbW92aWVzIGFyZSByYXRlZCwgYW5kIHRoZXNlIHJhdGluZ3MgYXJlIGNvbnNpc3RlbnQgZm9yIHZhcmlvdXMgYXVkaWVuY2VzLiBUaGUgZXhhbWluYXRpb24gb2YgcnVudGltZSBhbmQgcmFudGluZyBjYXRlZ29yaWVzIHJldmVhbHMgdGhhdCBtb3ZpZXMgYWltZWQgdG93YXJkcyB5b3VuZ3N0ZXJzIGFyZSB0eXBpY2FsbHkgc2hvcnRlci4NCg0KDQo=